/* Declarations for the type system interface.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_TYPES__
#define __KP_TYPES__   1

#include "interp.hpp"
#include "array.hpp"

KP_DECLS_BEGIN

struct instance : public finobj
{
  static const uint32_t init_flag = 1u << 20;
  static const uint32_t ishared_flag = 1u << 21;

  static const int code = typecode::INSTANCE;

  object ptype;
  object slots;
  object tspec;   // for type descriptor metatypes.
  object builtin;

  struct slotdef_iter
    {
      valref slotdefs;
      uint32_t idx;
      object bt_room[6];
      local_varobj<array> bt_mem;
      valref builtin;

      slotdef_iter (interpreter *interp, object inst);

      slotdef_iter (interpreter *interp, const slotdef_iter& right) :
          slotdefs (interp, *right.slotdefs), idx (right.idx),
          builtin (interp, UNBOUND)
        {
          if (*right.builtin == UNBOUND)
            return;

          copy_objs (this->bt_room, right.bt_room, KP_NELEM (this->bt_room));
          this->bt_mem.local_init (this->bt_room, KP_NELEM (this->bt_room));
          *this->builtin = this->bt_mem.as_obj ();
        }

      slotdef_iter& operator++ ();
      slotdef_iter operator++ (int);

      object operator* () const;

      bool valid ()
        {
          return (*this->slotdefs != UNBOUND || *this->builtin != UNBOUND);
        }
    };

  unsigned int type_code () const;
};

inline instance* as_instance (object obj)
{
  return ((instance *)unmask (obj));
}

inline bool instance_p (object obj)
{
  return (itype (obj) == typecode::INSTANCE);
}

inline bool typespec_p (object obj)
{
  return (instance_p (obj) && as_instance(obj)->tspec != UNBOUND);
}

inline bool builtin_typespec_p (object obj)
{
  return (typespec_p (obj) &&
    itype (as_instance(obj)->slots) == typecode::INT);
}

struct stream;
struct io_info;
struct pack_info;

// Return the type of OBJ.
KP_EXPORT object type (object obj);

// Create a new typespec from NAME, PARENTS and SLOTDEFS.
KP_EXPORT object type (interpreter *interp,
  object name, object parents, object slotdefs);

// Return the type-name of OBJ as a symbol.
KP_EXPORT object type_name (object obj);

// Return the inherited builtin member in OBJ.
KP_EXPORT object builtin_member (object obj);

// Allocate an instance from a typespec and arguments.
KP_EXPORT object alloc_inst (interpreter *interp,
  object xtype, object *argv, int argc);

// Get the value for slot NAME in instance INST.
KP_EXPORT object get_w (interpreter *interp,
  object inst, object name, object dfl);

// Set the value for slot NAME in instance INST to VAL.
KP_EXPORT object nput_w (interpreter *interp,
  object inst, object name, object va);

// Test if OBJ is of type TYPE.
KP_EXPORT int instanceof (object obj, object type);

// Test if T1 is a subtype of T2.
KP_EXPORT int subtype_p (object t1, object t2);

// Mutate a slot in an instance.
KP_EXPORT object nzap_w (interpreter *interp, object inst, object name,
  uint32_t flags, object fn, object *argv, int argc);

// Write an instance to a stream.
KP_EXPORT int write_w (interpreter *interp,
  stream *strm, object obj, io_info& info);

// Serialize an instance in a stream.
KP_EXPORT int64_t pack_w (interpreter *interp,
  stream *strm, object obj, pack_info& info);

// Deserialize an instance from a stream.
KP_EXPORT object unpack_w (interpreter *interp,
  stream *strm, pack_info& info, bool save);

// Return the typespec for a builtin typecode.
KP_EXPORT object builtin_type (unsigned int code);

// Init OP for types.
KP_EXPORT init_op init_types;

KP_DECLS_END

#endif
